#include "LynxSoundSource_C.hpp"

namespace Lynx
{
namespace Audio
{

SoundSource_C::SoundSource_C(AudioManager_C *manager, AudioReader *reader, bool streaming, const std::string &name) :
    mManager(manager),
    mName(name),
    mReader(reader),
    mStreaming(streaming)
{
    if(mStreaming)
    {
        ///
    }
    else
    {
        alGenBuffers(1, &mBuffer);
        uint32_t size = mReader->get_sample_count();
        uint16_t *data = new uint16_t[size];

        mFormat = mReader->get_audio_format();
        ALenum format = 0;

        mReader->read_samples(data, size);
        switch(mFormat.mChannelCount)
        {
            case 1:
                format = AL_FORMAT_MONO16;
                break;
            case 2:
                format = AL_FORMAT_STEREO16;
                break;
            case 4:
                format = alGetEnumValue("AL_FORMAT_QUAD16");
                break;
            case 6:
                format = alGetEnumValue("AL_FORMAT_51CHN16");
                break;
            case 7:
                format = alGetEnumValue("AL_FORMAT_61CHN16");
                break;
            case 8:
                format = alGetEnumValue("AL_FORMAT_71CHN16");
                break;
            default:
                format = 0;
                break;
        }


        alBufferData(mBuffer, format, data, size * sizeof(uint16_t), mFormat.mSamplesPerSecond);
    }
}

SoundSource_C::~SoundSource_C()
{
    for(std::list<Sound*>::iterator it = mConnectedSounds.begin(); it != mConnectedSounds.end(); it++)
    {
        if(!(*it)->decrement_counter())
            THROW_EXCEPTION(LEC_INTERNAL_ERROR, "Source is being deleted, but connected sound wants to be alive.");
    }

    mReader->decrement_counter();

    if(mStreaming)
    {
        //
    }
    else
    {
        alDeleteBuffers(1, &mBuffer);
    }

    mManager->internal_remove_source(this);
}

const std::string &SoundSource_C::get_name(void) const
{
    return mName;
}

void SoundSource_C::set_name(const std::string &new_name)
{
    if(mManager->get_source(new_name))
        THROW_EXCEPTION(LEC_INVALID_PARAMETER, "A source with that name already exists.");

    mName = new_name;
}

AudioFormat SoundSource_C::get_audio_format(void) const
{
    return mFormat;
}

bool SoundSource_C::is_streaming(void) const
{
    return mStreaming;
}

bool SoundSource_C::is_random_access(void) const
{
    return mStreaming ? mReader->is_random_access() : false;
}

ALuint SoundSource_C::get_samples_everything() const
{
    return mBuffer;
}

void SoundSource_C::grab_static(Sound *sound)
{
    if(!sound)
        THROW_EXCEPTION(LEC_INTERNAL_ERROR, "Can't connect source to a null-pointer.");

    for(std::list<Sound*>::iterator it = mConnectedSounds.begin(); it != mConnectedSounds.end(); it++)
        if(*it == sound)
            THROW_EXCEPTION(LEC_INTERNAL_ERROR, "Source is already connected to this sound.");

    mConnectedSounds.push_back(sound);
    increment_counter();
}

void SoundSource_C::release_static(Sound *sound)
{
    if(!sound)
        THROW_EXCEPTION(LEC_INTERNAL_ERROR, "Can't release source from a null-pointer.");

    for(std::list<Sound*>::iterator it = mConnectedSounds.begin(); it != mConnectedSounds.end(); it++)
    {
        if(*it == sound)
        {
            mConnectedSounds.erase(it);
            decrement_counter();
            return;
        }
    }

    THROW_EXCEPTION(LEC_INTERNAL_ERROR, "Couldn't release from non-connected sound.");
}

} // namespace Audio
} // namespace Lynx
